/*
 *  FAT file ops Initialization
 *
 *  Copyright(C) 2001
 *  Camilo Alejandro Arboleda
 *
 *  The contents of this file are distributed under the GNU General
 *  Public License version 2.
 *
 *  As a special exception, when this code is included in the RTEMS
 *  operating system, linking other files with RTEMS objects including
 *  this code does not cause the resulting executable application to
 *  be covered by the GNU General Public License. This exception does
 *  not however invalidate any other reasons why the executable file might
 *  be covered by the GNU General Public License.
 */

#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "fat.h"

extern void update_cluster(unsigned cluster, unsigned new_value, fat_geom_t *geom);
extern void save_fat( fat_geom_t *geom);
extern int update_dir_entry(FAT_jnode_t *node);
extern int rtems_libio_is_file_open(void  *node_access);

#ifndef set_errno_and_return_minus_one
#define set_errno_and_return_minus_one( _error ) \
  do { errno = (_error); return -1; } while(0)
#endif

/*-------------------------------------------------------------------
 * Open file
 */
int
FAT_file_open(iop, pathname, flag, mode)
   rtems_libio_t  *iop;
   const char *pathname;
   unsigned flag;
   unsigned mode;
{
   FAT_jnode_t *node = iop->file_info;

   node->reference++;
   iop->offset = 0;
   iop->size   = node->size;

   return 0;
}

/*-------------------------------------------------------------------
 * Close file
 */
int
FAT_file_close(iop)
   rtems_libio_t  *iop;
{
   FAT_jnode_t *node = iop->file_info;

   node->reference--;
   if (node->reference <= 0)
      del_node(node);

   return 0;
}

/*-------------------------------------------------------------------
 * Read file
 */

int
FAT_file_read(iop, buffer, count)
   rtems_libio_t  *iop;
   void *buffer;
   int count;
{
   int bytes_moved = 0;
   FAT_jnode_t *node = iop->file_info;

   rtems_semaphore_obtain( node->geometry->rw_mutex_id,
                           RTEMS_WAIT,RTEMS_NO_TIMEOUT );
   if (iop->offset != node->offset) {
      node->offset = iop->offset;
      /* node->current_cluster = find_cluster(node,node->offset); */
   }
   bytes_moved = fat_read(node, (char *)buffer, count);
   rtems_semaphore_release( node->geometry->rw_mutex_id );

   return bytes_moved;
}



/* -----------------------------------------------------------------------
 * This routine will behave in one of three ways based on the state of 
 * argument whence. Based on the state of its value the offset argument will
 * be interpreted using one of the following methods:
 *
 * 	SEEK_SET - offset is the absolute byte offset from the start of the
 *                 logical start of the dirent sequence that represents the
 *                 directory
 * 	SEEK_CUR - offset is used as the relative byte offset from the current
 *                 directory position index held in the iop structure
 *	SEEK_END - N/A --> This will cause an assert.
 */

int
FAT_file_lseek( rtems_libio_t  *iop, off_t offset, int whence )
{
   return 0;
#if 0
   FAT_jnode_t *node = iop->file_info;

   rtems_semaphore_obtain( node->geometry->rw_mutex_id,
                           RTEMS_WAIT,RTEMS_NO_TIMEOUT );

   switch( whence )
   {
   case SEEK_SET:	/* absolute move from the start of the file */
      iop->offset = offset;
      break;

   case SEEK_CUR:	/* relative move */
      iop->offset = iop->offset + offset;
      break;

   case SEEK_END:	/* Movement past the end of the file via lseek */
      iop->offset = iop->size - offset;
      break;

   default:
      rtems_semaphore_release( node->geometry->rw_mutex_id );
      set_errno_and_return_minus_one( EINVAL );
      break;

   }

   rtems_semaphore_release( node->geometry->rw_mutex_id );
   return 0;
#endif
}


int
FAT_file_stat(rtems_filesystem_location_info_t *loc, struct stat *buf )
{
  FAT_jnode_t   *node;

  node = loc->node_access;


  buf->st_size = node->size;

  buf->st_dev   = node->geometry->fd;
  buf->st_rdev  = node->geometry->fd;
  buf->st_mode  = S_IRWXU | S_IRWXG | S_IRWXO | S_IWRITE | S_IREAD | S_IEXEC;
  buf->st_nlink = 1;
  buf->st_ino   = node->st_ino;
  buf->st_uid   = 0;
  buf->st_gid   = 0;

  buf->st_atime = node->stat_atime;
  buf->st_mtime = node->stat_mtime;
  buf->st_ctime = node->stat_ctime;

  return 0;
}

/*
 *  memfile_write
 *
 *  This routine processes the write() system call.
 */

int
FAT_file_write( rtems_libio_t *iop, const void *buffer, unsigned32 count)
{
   FAT_jnode_t     *node;
   int             status;

   node = iop->file_info;

   rtems_semaphore_obtain( node->geometry->rw_mutex_id,
                           RTEMS_WAIT,RTEMS_NO_TIMEOUT );

   if (iop->offset != node->offset) {
      node->offset = iop->offset;
      /* node->current_cluster = find_cluster(node,node->offset); */
   }
   status = fat_write( node, (char *)buffer, count );
   if (iop->size != node->size) {
      iop->size = node->size;
      update_dir_entry(node);
      save_fat(node->geometry);
   };

   rtems_semaphore_release( node->geometry->rw_mutex_id );
   return status;
}

int
FAT_file_truncate( rtems_libio_t *iop, off_t length)
{
   FAT_jnode_t   *node = iop->file_info;
   fat_geom_t  *geom;
   int         cluster, next_cluster;

   geom = node->geometry;

   /*
    * The file cannot be open to free.
    */

   rtems_semaphore_obtain(geom->mutex_id,RTEMS_WAIT,RTEMS_NO_TIMEOUT);
   cluster = node->st_ino;
   do {
      next_cluster = search_next_cluster(cluster, geom);
      update_cluster(cluster, 0x0000, geom);
      cluster = next_cluster;
   } while (cluster < 0xfff0);
   update_cluster(node->st_ino, 0xffff, geom);
   node->size   = iop->size   = 0;
   node->offset = iop->offset = 0;

   update_dir_entry(node);

   save_fat(geom);

   rtems_semaphore_release(geom->mutex_id);

   return 0;

}

